#ifndef _NE_RESOURCE_H_
#define _NE_RESOURCE_H_

#include <common/SharedPtr.h>
#include <system/Mutex.h>

namespace ne
{
    enum LoadingState
    {
        // Not loaded
        LOADING_STATE_UNLOADED,
        // Loading is in progress
        LOADING_STATE_LOADING,
        // Fully loaded
        LOADING_STATE_LOADED,
        // Currently unloading
        LOADING_STATE_UNLOADING,
        // Fully prepared
        LOADING_STATE_PREPARED,
        // Preparing is in progress
        LOADING_STATE_PREPARING,
        // Downloading is in progress
        LOADING_STATE_DOWNLOADING
    };

    class ResourceManager;
    class ManualResourceLoader;

    class NEAPI Resource
    {
        friend class ResourceManager;

        public:
            class Listener
            {
                public:
                    Listener() {}
                    virtual ~Listener() {}
                    virtual void onPreparingComplete(Resource *pResource) {}
                    virtual void onLoadingComplete(Resource *pResource) {}
                    virtual void onUnloadingComplete(Resource *pResource) {}
                    virtual void onDownloadingComplete(Resource *pResource) {}
                    virtual void onBackgroundThreadPreparingComplete(Resource *pResource) {}
                    virtual void onBackgroundThreadLoadingComplete(Resource *pResource) {}
                    virtual void onBackgroundThreadUnloadingComplete(Resource *pResource) {}
                    virtual void onBackgroundThreadDownloadingComplete(Resource *pResource) {}
            };

            typedef std::list<Listener*> ListenerList;
            typedef ListenerList::iterator ListenerListIterator;
            typedef ListenerList::const_iterator ListenerListConstIterator;

        public:
            Resource(ResourceManager *pManager,
                     const uint64_t id,
                     const std::string &name,
                     const std::string &fileName="",
                     const bool isManual=false,
                     ManualResourceLoader *pLoader=0);

            virtual ~Resource();

            virtual bool prepare(const bool backgroundThread=false);

            virtual bool load(const bool backgroundThread=false);

            virtual bool unload(const bool backgroundThread=false);

            virtual bool reload(const bool backgroundThread=false);

            virtual bool escalateLoading();

            virtual bool download(const bool backgroundThread=false);

            virtual ResourceManager* getManager() const
            {
                return mpManager;
            }

            virtual ManualResourceLoader* getLoader() const
            {
                return mpLoader;
            }

            virtual uint64_t getID() const
            {
                return mID;
            }

            virtual const std::string& getName() const
            {
                return mName;
            }

            virtual void setFileName(const std::string &fileName)
            {
                mFileName = fileName;
            }

            virtual const std::string& getFileName() const
            {
                return mFileName;
            }

            virtual size_t getSize() const
            {
                return mSize;
            }

            virtual bool isReloadable() const
            {
                return !mIsManual || mpLoader;
            }

            virtual bool isManual() const
            {
                return mIsManual;
            }

            virtual LoadingState getLoadingState() const
            {
                return mLoadingState;
            }

            virtual bool isPreparing() const
            {
                return (mLoadingState == LOADING_STATE_PREPARING);
            }

            virtual bool isPrepared() const
            {
                return (mLoadingState == LOADING_STATE_PREPARED);
            }

            virtual bool isLoading() const
            {
                return (mLoadingState == LOADING_STATE_LOADING);
            }

            virtual bool isLoaded() const
            {
                return (mLoadingState == LOADING_STATE_LOADED);
            }

            virtual bool isUnoading() const
            {
                return (mLoadingState == LOADING_STATE_UNLOADING);
            }

            virtual bool isUnloaded() const
            {
                return (mLoadingState == LOADING_STATE_UNLOADED);
            }

            virtual bool isDownloading() const
            {
                return (mLoadingState == LOADING_STATE_DOWNLOADING);
            }

            virtual void setBackgroundThreadLoading(const bool backgroundThread)
            {
                mIsBackgroundThreadLoading = backgroundThread;
            }

            virtual bool isBackgroundThreadLoading() const
            {
                return mIsBackgroundThreadLoading;
            }

            // TODO: Do not add too many Listeners
            virtual void addListener(Listener *pListener);
            virtual void removeListener(Listener *pListener);

            virtual FILE* openFile(const char* mode);
            virtual int closeFile(FILE *file);

            virtual const char* type() const
            {
                return typeid(*this).name();
            }

        protected:
            virtual void onPreLoad() {}
            virtual void onPostLoad() {}

            virtual void onPreUnload() {}
            virtual void onPostUnload() {}

            virtual void onPreDownload() {}
            virtual void onPostDownload() {}

            virtual bool prepareImplement() { return true; }
            virtual bool loadImplement() = 0;
            virtual bool unloadImplement() = 0;
            virtual bool downloadImplement() { return true; }

            virtual size_t _calculateSize() const = 0;

            virtual void _firePreparingComplete(const bool backgroundThread);

            virtual void _fireLoadingComplete(const bool backgroundThread);

            virtual void _fireUnloadingComplete(const bool backgroundThread);

            virtual void _fireDownloadingComplete(const bool backgroundThread);

        private:
            Resource(const Resource &);

            Resource& operator = (const Resource &);

        protected:
            Mutex mMutex;
            ResourceManager *mpManager;
            uint64_t mID;
            std::string mName;
            std::string mFileName;
            size_t mSize;
            bool mIsManual;
            ManualResourceLoader *mpLoader;
            volatile LoadingState mLoadingState;
            volatile bool mIsBackgroundThreadLoading;
            ListenerList mListenerList;
    };

    typedef SharedPtr<Resource> ResourcePtr;
    typedef std::map<std::string, ResourcePtr> ResourceMap;
    typedef ResourceMap::iterator ResourceMapIterator;
    typedef ResourceMap::const_iterator ResourceMapConstIterator;

    typedef std::map<uint64_t, ResourcePtr> ResourceIDMap;
    typedef ResourceIDMap::iterator ResourceIDMapIterator;
    typedef ResourceIDMap::const_iterator ResourceIDMapConstIterator;

    class NEAPI ManualResourceLoader
    {
        public:
            ManualResourceLoader() {}

            virtual bool prepareResource(Resource* resource) = 0;

            virtual bool loadResource(Resource* resource) = 0;

            virtual ~ManualResourceLoader() {}
    };
}

#endif
